package edu.ntnu.superrss.controller;

import com.sun.syndication.fetcher.FetcherException;
import com.sun.syndication.io.FeedException;
import edu.ntnu.superrss.controller.domain.FormFeed;
import edu.ntnu.superrss.models.SyndEntryImpl;
import edu.ntnu.superrss.models.SyndFeedImpl;
import edu.ntnu.superrss.models.User;
import edu.ntnu.superrss.service.FeedService;
import edu.ntnu.superrss.service.UserService;
import edu.ntnu.superrss.utils.SyndEntrySort;
import edu.ntnu.superrss.utils.SyndFeedSort;
import java.io.IOException;
import java.net.MalformedURLException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpSession;
import org.hibernate.HibernateException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.SessionAttributes;

/**
 * FeedController is a controller class for various actions.
 * It uses annotations and auto-wiring i Spring to implement the neccesarry
 * mappings to map the controller to functions and views.
 *
 * 
 */
@Controller
@SessionAttributes("user")
/**
 *  The FeedController uses auto-wiring to setup the FeedService and UserService.
 *  The various services are used to modulerize needed functions. A session attribute is
 *  used in the default implementation to transfer relevant data
 *
 */
public class FeedController {

    private FeedService fs;
    private UserService us;

    @Autowired
    public void setFeedService(FeedService fs) {
        this.fs = fs;
    }

    @Autowired
    public void setUserService(UserService us) {
        this.us = us;
    }

    /**
     * Maps the removeFeed.htm POST to the postFeedsRemove function. Handles POST-data from the removeFeed view.
     * <p>
     * Thos method handles the POST for the removeFeed view. It checks if a user is set before removing
     * the feed from the user. It catches the exceptions that might occur from the FeedService used
     *
     * @param id        the Long containing the id of the feed to be removed
     * @param session   the session containing user data
     * @param model     the ModelMap object used to transfer data to the view
     * @return          the String containing the name of the view to return
     */
    @RequestMapping(value = "/remove-feed.htm", method = RequestMethod.GET)
    public String removeFeed(@RequestParam("id") Long id, HttpSession session, ModelMap model) {
        Logger.getLogger(FeedController.class.getName()).log(Level.INFO, "FeedController:removeFeed called");
        String semiUser;

        //Have to make sure the session is OK and the user attribute is present
        if (((((User) (session.getAttribute("user"))).getUsername()) != null)) {
            semiUser = ((User) session.getAttribute("user")).getUsername();
        } else {
            Logger.getLogger(FeedController.class.getName()).log(Level.INFO, "FeedController:removeFeed FAILED:Could not get the user info from the session");
            model.addAttribute("errors", "Missing user info");
            return "redirect:feeds.htm";
        }
        
        try {
            fs.removeFeed(semiUser, id);
        } catch (NullPointerException e) {
            Logger.getLogger(FeedController.class.getName()).log(Level.INFO, "FeedController:removeFeed FAILED:Could not find the user in the datasbase", e);
            model.addAttribute("errors", "Something was null");
            model.addAttribute("errors", e.toString());
        } catch (HibernateException e) {
            Logger.getLogger(FeedController.class.getName()).log(Level.INFO, "FeedController:removeFeed FAILED:", e);
            model.addAttribute("errors", "Hibernate failed");
            model.addAttribute("errors", e.toString());
        } catch (Exception e) {
            Logger.getLogger(FeedController.class.getName()).log(Level.INFO, "FeedController:removeFeed FAILED:", e);
            model.addAttribute("errors", "Something failed");
            model.addAttribute("errors", e.toString());
        }
        return "redirect:feeds.htm";
    }

    @RequestMapping(value = "/feed.htm" ,method = RequestMethod.GET)
    public String viewFeed(@RequestParam("id") Long id, ModelMap model, HttpSession httpSession) {

        try {
            SyndFeedImpl syndFeed = this.fs.getFeed(id);
            User user = this.us.getUser((User) httpSession.getAttribute("user"));
            List<SyndEntryImpl> unreadEntries = new ArrayList<SyndEntryImpl>(syndFeed.getEntries());
            unreadEntries.removeAll(user.getEntriesRead());

            Collections.sort(unreadEntries, SyndEntrySort.DATE_ORDER);

            //Changes the feed entries in the object, but doesn't update database.
            //This way all users will see a different list of entries read/unread
            syndFeed.setEntries(new ArrayList<SyndEntryImpl>(unreadEntries));

            model.addAttribute("title", "My subscriptions");
            model.addAttribute("feed", syndFeed);
            return "feed";
        } catch (NullPointerException e) {
            System.err.println(e);
            return "error";
        }
    }


    /**
     *  Maps the addFeed.htm POST to the processSubmit function. Handles POST data when adding a new feed
     * to the user.
     * <p>
     * This method handles POST for the addFeed view. It checks if a user is set before calling the
     * FeedService to add the feed to the user. It catches the exceptions that might occur when using
     * the FeedService
     *
     *
     * @param url       the URL of the feed to add
     * @param user      depreciated
     * @param session   the session containing user data. The
     * @param model     the ModelMap object used to transfer data to the view
     * @return          the String containing the name of the view to return
     */
    @RequestMapping(value = "/feeds.htm", method = RequestMethod.POST)
    public String addFeed(@ModelAttribute("url") String url, @ModelAttribute("user") User user, ModelMap model, HttpSession session) {

        Logger.getLogger(FeedController.class.getName()).log(Level.INFO, "FeedController:addFeed called");
        String semiUser;

        //Have to make sure the session is OK and the user attribute is present
       if (((((User) (session.getAttribute("user"))).getUsername()) != null)) {
            semiUser = ((User) session.getAttribute("user")).getUsername();
        } else {
            Logger.getLogger(FeedController.class.getName()).log(Level.INFO, "FeedController:addFeed:FAILED:Could not get the user info from the session");
            model.addAttribute("errors", "Missing user info");
            return "feeds";
        }
        try {
            fs.addFeed(semiUser, url);
        } catch (MalformedURLException e) {
            model.addAttribute("errors", "Malformed URL");
            model.addAttribute("errors", e.toString());
        } catch (HibernateException e) {
            model.addAttribute("errors", "Hibernate failed");
            model.addAttribute("errors", e.toString());
        } catch (FeedException e) {
            model.addAttribute("errors", "Feed fail");
            model.addAttribute("errors", e.toString());
        } catch (FetcherException e) {
            model.addAttribute("errors", "Fetcher failed");
            model.addAttribute("errors", e.toString());
        } catch (IOException e) {
            model.addAttribute("errors", "IO fail");
            model.addAttribute("errors", e.toString());
        } catch (IllegalArgumentException e) {
            model.addAttribute("errors", "Bad url");
            model.addAttribute("errors", e.toString());
        } catch (Exception e) {
            model.addAttribute("errors", "Something went wrong");
            model.addAttribute("errors", e.toString());
        }

        model.addAttribute("formFeed", new FormFeed());
        return "redirect:feeds.htm";
    }

    @RequestMapping(value = "/feeds.htm", method = RequestMethod.GET)
    public String viewFeeds(@RequestParam(required = false, value = "by") String by, HttpSession session, ModelMap model) {
      Logger.getLogger(FeedController.class.getName()).log(Level.INFO, "FeedController:viewFeeds called");
        String username;

        if (((((User) (session.getAttribute("user"))).getUsername()) != null)) {
            username = ((User) session.getAttribute("user")).getUsername();

        } else {
            Logger.getLogger(FeedController.class.getName()).log(Level.INFO, "FeedController:viewFeeds FAILED:Could not get the user info from the session");
            model.addAttribute("errors", "Missing user info");
            return "redirect:register.htm";
        }

//        List l = new ArrayList();
        List<SyndFeedImpl> l = new ArrayList<SyndFeedImpl>(fs.getFeeds(username));
        if (by != null && by.equals("byTitle")) {
            Collections.sort(l, SyndFeedSort.TITLE_ORDER);
        } else {
            Collections.sort(l, SyndFeedSort.DATE_ORDER);
        }

        model.addAttribute("title", "My subscriptions");
        model.addAttribute("formFeed", new FormFeed());
        model.addAttribute("feeds", l);
        return "feeds";
    }
}
